// 虚拟dom
function createElement(type,props,...children) {
    return {
        type,
        props: {
            ...props,
            children:children.map((child) => {
                const isTextNode = typeof child === 'string' || typeof child === 'number' 
                return isTextNode ? createTextNode(child) : child
            })               
        }
    }
}

function render(el,container) {
    wipRoot = {
        dom:container,
        props:{
            children:[el]
        }
    }
    nextWorkOfUnit = wipRoot
}

function update() {
    // 通过闭包获取当前的更新节点函数
    // 处理更新的开始节点
    let currentFibber = wipFiber
    return () => {
        console.log('currentFibber',currentFibber);
        wipRoot = {
            ...currentFibber,
            alternate:currentFibber
        }
        nextWorkOfUnit = wipRoot
    } 
}

function createTextNode(text) {
    return {
        type: 'TEXT_ELEMENT',
        props:{
            nodeValue: text,
            children:[]
        }
    }
}

// 任务调度器
let wipRoot = null;
let currentRoot = null;
let nextWorkOfUnit = null
let deletions = []
let wipFiber = null
function workLoop(deadline) {
    let shouldYield = false
    while (!shouldYield && nextWorkOfUnit) {
        // 任务执行
        nextWorkOfUnit = performWorkOfUnit(nextWorkOfUnit)
        if(wipRoot?.sibling?.type  === nextWorkOfUnit?.type) {
            nextWorkOfUnit = undefined
        }
        shouldYield = deadline.timeRemaining() < 1;
    }
    // 节点都转化完后
    if(!nextWorkOfUnit && wipRoot) {
        commitRoot()
        // 看useEffect之后，是否有值更新从而触发新的一次视图更新
        if(nextWorkOfUnit) {
            wipRoot = currentRoot
        }
    }
    requestIdleCallback(workLoop)
}


function commitRoot() {
    console.log('----------------');
    commitWork(wipRoot.child)
    commitEffectHooks()
    deletions.forEach(commitDeletions)
    currentRoot = wipRoot
    wipRoot = null
    deletions = []
}

function commitEffectHooks() {
    function run(fiber) {
        if(!fiber) return
        if(!fiber.alternate) {
            // init
            fiber.effectHooks?.forEach(hook => {
                hook.cleanup = hook.callback()
            })
        } else {
            fiber.effectHooks?.forEach((newHook,index) => {
                const oldEffectHook = fiber.alternate?.effectHooks[index]
                
                // some
                const needUpdate = oldEffectHook?.deps.some((oldDep,i) => {
                    return oldDep !== newHook.deps[i]
                })
                needUpdate && (newHook.cleanup = newHook?.callback())
            })
        }
        run(fiber.child)
        run(fiber.sibling)
    }
    function runCleanup(fiber) {
        if(!fiber) return 
        fiber.alternate?.effectHooks?.forEach(hook => {
            if(hook.deps.length > 0) {
                hook.cleanup && hook.cleanup()
            }
        })
        runCleanup(fiber.child)
        runCleanup(fiber.sibling)
    }
    runCleanup(wipRoot)
    run(wipRoot)
}

function commitDeletions(fiber) {
    if(fiber.dom) {
        let fiberParent = fiber.parent
        while(!fiberParent.dom) {
            fiberParent = fiberParent.parent
        }
        fiberParent.dom.removeChild(fiber.dom)
    } else {
        commitDeletions(fiber.child)
    }
} 

function commitWork(fiber) { 
    if(!fiber) return
    let fiberParent = fiber.parent
    while(!fiberParent.dom) {
        fiberParent = fiberParent.parent
    }

    if(fiber.effectTag === 'update' && fiber.dom) {
        updateProps(fiber.dom,fiber.props,fiber?.alternate.props)
    } else if(fiber.effectTag === 'placement') {
        if(fiber.dom) {
            fiberParent.dom.append(fiber.dom)
        }
    }
    
    commitWork(fiber.child)
    commitWork(fiber.sibling)
}

// 创建dom
function createDom (type) {
    return type === 'TEXT_ELEMENT' ? document.createTextNode("") : document.createElement(type)
}

// 处理props
function updateProps(dom,nextProps,prevProps) {
    // 老的有，新的没有 删除
    Object.keys(prevProps).forEach((key) => {
        if(key !== 'children') {
            if(!(key in nextProps)) {
                dom.removeAttribute(key)
            }
        }
    })
    // 老的没有，新的有 添加
    // 老的新的都有 修改
    Object.keys(nextProps).forEach((key) => {
        if(key !== 'children') {
            if(nextProps[key] !== prevProps[key]) {
                if(key.startsWith('on')) {
                    const eventType = key.slice(2).toLocaleLowerCase()
                    dom.removeEventListener(eventType,prevProps[key])
                    dom.addEventListener(eventType,nextProps[key])
                } else {
                    dom[key] = nextProps[key]
                }
            }
        }
    })  
}

// 处理children
function reconcileChildren(fiber,children) {
    let oldFiber = fiber.alternate?.child
    let prevChild = null
    children.forEach((child,index) => {
        const isSameType = oldFiber && oldFiber.type === child.type
        let newFiber
        if(isSameType) {
            // update
            newFiber = {
                type:child.type,
                props:child.props,
                child:null,
                parent:fiber,
                sibling:null,
                dom:oldFiber.dom,
                alternate:oldFiber,
                effectTag:'update'
            }
        } else {
            if(child) {
                newFiber = {
                    type:child.type,
                    props:child.props,
                    child:null,
                    parent:fiber,
                    sibling:null,
                    dom:null,
                    effectTag:'placement'
                }
            }
            if(oldFiber) {
                deletions.push(oldFiber)
            }
        }
        if(oldFiber) {
            oldFiber = oldFiber.sibling
        }
        if(index === 0) {
            fiber.child = newFiber
        } else {
            prevChild.sibling = newFiber
        }
        if(newFiber) {
            prevChild = newFiber
        }
    })
    while(oldFiber) {
        deletions.push(oldFiber)
        oldFiber = oldFiber.sibling
    }
}

// 处理Function Component
function updateFunctionComponent(fiber) {
    stateHooks = []
    stateHookIndex = 0
    effectHooks = []
    // 将当前更新的组件赋值给全局变量便于update单独树更新
    wipFiber = fiber
    // 3.转换链表 设置好指针
    const children = [fiber.type(fiber.props)]
    reconcileChildren(fiber,children)
}

// 处理dom节点
function updateHostComponent(fiber) {
    if(!fiber.dom) {
        // 1.创建dom
        const dom = (fiber.dom = createDom(fiber.type))
        // 2.处理props
        updateProps(dom,fiber.props,{})
    }
    const children = fiber.props.children
    reconcileChildren(fiber,children)
}


function performWorkOfUnit(fiber) {
    const isFunctionComponent =  typeof fiber.type == 'function'
    if(isFunctionComponent) {
        updateFunctionComponent(fiber)
    } else {
        updateHostComponent(fiber)
    }

    // 4.返回下一个要执行的任务
    if(fiber.child) {
        return fiber.child
    } 
    let nextFiber = fiber
    while(nextFiber) {
        if(nextFiber.sibling) return nextFiber.sibling
        nextFiber = nextFiber.parent
    }
}

requestIdleCallback(workLoop)

let stateHooks
let stateHookIndex
function useState(initial) {
    let currentFibber = wipFiber
    const oldHook = currentFibber.alternate?.stateHooks[stateHookIndex]
    const stateHook = {
        state: oldHook ? oldHook.state : initial,
        queue: oldHook ? oldHook.queue : []
    }
    stateHook.queue.forEach((action) => {
        stateHook.state = action(stateHook.state)
    })
    stateHook.queue = []
    stateHookIndex++;
    stateHooks.push(stateHook)

    currentFibber.stateHooks = stateHooks

    function setState(action) {
        // 提前检测，值是否相同
        const eagerState = typeof action === 'function' ? action(stateHook.state) : action
        if(stateHook.state === eagerState) return
        stateHook.queue.push(typeof action === 'function' ? action : () => action)
        // 调用update更新逻辑
        wipRoot = {
            ...currentFibber,
            alternate:currentFibber
        }
        nextWorkOfUnit = wipRoot
    }
    return [
        stateHook.state,
        setState
    ]
}

let effectHooks
function useEffect(callback,deps) {
    const effectHook  = {
        callback,
        deps,
        cleanup:undefined
    }
    effectHooks.push(effectHook)
    wipFiber.effectHooks = effectHooks
}

const React = {
    useEffect,
    useState,
    update,
    render,
    createElement
}

export default React
